#include "graphicsclass.h"

//#define _DEBUGAPP 1


GraphicsClass::GraphicsClass(): m_blurEnabled(BLUR_ENABLED), m_D3D(0), m_Camera(0), m_TessShader(0), m_DOF(0)
{
	m_Models = new ModelsArray();
	m_Models->reserve(10);
	m_Lights = new LightsContainer();
}

bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
	bool result;
	
	m_D3D = new D3DClass;
	result = m_D3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
	if(!result)
	{
		MessageBox(hwnd, L"Could not initialize Direct3D.", L"Error", MB_OK);
		return false;
	}

	m_Camera = new CameraClass(CAMERA_INIT_POSITION);

	m_Material	=  new Material();
	m_Material->mKa = D3DXVECTOR4(0.1f,0.1f,0.1f,1.0f);	// Coefficiente di riflessione ambientale
	m_Material->mKd = D3DXVECTOR4(0.5f,0.5f,0.5f,1.0f);		// Coefficiente di riflessione diffusiva
	m_Material->mKe = D3DXVECTOR4(0.0f,0.0f,0.0f,1.0f);		// Coefficiente di riflessione emissiva
	m_Material->mKs = D3DXVECTOR4(0.5f,0.5f,0.5f,1.0f);		// Coefficiente di riflessione speculare
	m_Material->mShininess = 16;							// esponente shininess

	HRESULT hr = D3DX11CreateShaderResourceViewFromFile( m_D3D->GetDevice(), L"rocks.jpg", NULL, NULL, &m_Material->m_pTextureColorRV, NULL );
	if( FAILED(hr))
	{
		return false;
	}
	hr = D3DX11CreateShaderResourceViewFromFile( m_D3D->GetDevice(), L"rocks_NM_height.dds", NULL, NULL, &m_Material->m_pTextureNormalRV, NULL );
	if( FAILED(hr))
	{
		return false;
	}

	ModelClass* cube = 0;
	for(int i=0; i<10; ++i)
	{
		cube = new ModelClass(-20.0f + (i * 5.0f), 0.0f, 15.0f, 1.f, 1.f, 1.f);
		
		result = cube->Initialize(m_D3D->GetDevice());
		if(!result)
		{
			MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
			return false;
		}
		cube->SetMaterial(m_Material);
		m_Models->push_back(cube);
	}

	LightClass* light1 = new LightClass();
	LightClass* light2 = new LightClass();
	
	light1->SetAmbientColor(5,5,5,255);
	light1->SetDiffuseColor(255,0,0,255);
	light1->SetDirection(0,0,-1);
	light1->SetPosition(-5, 10, 15);

	light2->SetAmbientColor(10,10,10,255);
	light2->SetDiffuseColor(0,255,0,255);
	light2->SetDirection(0,0,-1);
	light2->SetPosition(5, 10, 15);

	m_Lights->AddLight(light1);
	m_Lights->AddLight(light2);

	/*light placeholders*/
	{
		cube = new ModelClass(-5, 10, 15, 0.2f, 0.2f, 0.2f);
		result = cube->Initialize(m_D3D->GetDevice());
		if(!result)
		{
			MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
			return false;
		}
		cube->SetMaterial(m_Material);
		m_Models->push_back(cube);

		cube = new ModelClass(5, 10, 15, 0.2, 0.2, 0.2);
		result = cube->Initialize(m_D3D->GetDevice());
		if(!result)
		{
			MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
			return false;
		}
		cube->SetMaterial(m_Material);
		m_Models->push_back(cube);
	}

	m_TessShader = new TesselationShaderClass;
	result = m_TessShader->Initialize(m_D3D->GetDevice());
	if(!result)
	{
		MessageBox(hwnd, L"Could not initialize the tess shader object.", L"Error", MB_OK);
		return false;
	}

	m_DOF = new DOFShaderClass();
	result = m_DOF->Initialize(m_D3D->GetDevice(),hwnd,screenWidth,screenHeight);
	if(!result)
	{
		MessageBox(hwnd, L"Could not initialize the DOF shader object.", L"Error", MB_OK);
		return false;
	}
	
	return true;
}

void GraphicsClass::Shutdown()
{
	if(m_TessShader)
	{
		delete m_TessShader;
		m_TessShader = 0;
	}

	if(m_Models)
	{
		ModelsArray::iterator model = m_Models->begin();
		for(; model != m_Models->end(); ++model )
		{
			(*model)->Shutdown();
			delete (*model);
			(*model) = 0;
		}
		m_Models->clear();
	}

	if(m_Camera)
	{
		delete m_Camera;
		m_Camera = 0;
	}

	if (m_DOF)
	{
		delete m_DOF;
		m_DOF = 0;
	}

	if(m_D3D)
	{
		m_D3D->Shutdown();
		delete m_D3D;
		m_D3D = 0;
	}

	return;
}

bool GraphicsClass::Frame(InputClass* input)
{
	bool result;

	if(input->IsKeyDown('W') || input->IsKeyDown('w') )
	{
		if (m_D3D->GetRenderType() == RENDERTYPE_SOLID)
		{
			m_D3D->SetRenderWireframe();
		}
		else
		{
			m_D3D->SetRenderSolid();
		}
	}	

	m_Camera->Update(input);
	
	result = PreRender();
	if (!result)
		return false;

	result = Render();
	if (!result)
		return false;

	result = PostRender();

	return result;
}

bool GraphicsClass::RenderWorld()
{
	D3DXMATRIX worldMatrix,viewMatrix, projectionMatrix;
	bool result;

	m_D3D->BeginScene(0.20f, 0.20f, 0.2f, 0.0f);

	m_Camera->Render();

	m_Camera->GetViewMatrix(viewMatrix);
	m_D3D->GetProjectionMatrix(projectionMatrix);

	UINT num_models = m_Models->size();
	for(UINT idx = 0  ; idx != num_models; ++idx)
	{
		ModelClass* model = (*m_Models)[idx];
		model->Render(m_D3D->GetDeviceContext());
		worldMatrix = model->GetTransformationMatrix();	

		m_D3D->GetDeviceContext()->PSSetShaderResources(0, 1, &model->GetMaterial()->m_pTextureColorRV);
		m_D3D->GetDeviceContext()->PSSetShaderResources(1, 1, &model->GetMaterial()->m_pTextureNormalRV);

		m_D3D->GetDeviceContext()->DSSetShaderResources(0, 1, &model->GetMaterial()->m_pTextureColorRV);
		m_D3D->GetDeviceContext()->DSSetShaderResources(1, 1, &model->GetMaterial()->m_pTextureNormalRV);

		result = m_TessShader->Render(m_D3D->GetDeviceContext(), model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, m_Camera->GetPosition(), m_Lights, model->GetMaterial());
		if(!result)
		{
			return false;
		}
	}

	return true;
}

bool GraphicsClass::PreRender()
{
	m_DOF->PreRender(m_D3D);
	return true;
}

bool GraphicsClass::Render()
{
	D3DXMATRIX worldMatrix, viewMatrix, projMatrix;
	bool result = false;

	m_Camera->GetStaticViewMatrix(viewMatrix);
	m_D3D->GetWorldMatrix(worldMatrix);
	m_D3D->GetProjectionMatrix(projMatrix);

#ifdef _DEBUGAPP
	m_D3D->SetRenderWireframe();
#endif

	result = RenderWorld();
	if (!result)
	{
		return false;
	}

#ifdef _DEBUGAPP
	m_D3D->SetRenderSolid();
#endif

	result = m_DOF->Render(m_D3D,worldMatrix,viewMatrix,projMatrix );

	return result;
}

bool GraphicsClass::PostRender()
{
	bool result = m_DOF->PostRender(m_D3D);
	m_D3D->EndScene();
	return result;
}
